/*****************************************************************************
 * hal_opl1x_gpio.c
 *
 * Copyright (C) 2019 Jeasonvor <1101627719@qq.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ****************************************************************************/

/*****************************************************************************
 * Included Files
 ****************************************************************************/

#include "plum_api.h"
#include "hal/source/hal_opl1x/hal_opl1x.h"

/*****************************************************************************
 * Trace Definitions
 ****************************************************************************/
#define LOG_RUN_LEVEL LOG_LEVEL_DEBUG
#define LOG_MODULE    "hal.gpio"
#include "thirdparty/log/log.h"
/*****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/*****************************************************************************
 * Private Types
 ****************************************************************************/

typedef struct {
    plum_u32 id;
    plum_void (*func)(plum_u32 id);
    struct list_head list;
} hal_gpio_irq_t;

/*****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/*****************************************************************************
 * Private Data
 ****************************************************************************/

PLUM_PRIVATE hal_gpio_irq_t gpio_irq_head;

/*****************************************************************************
 * Public Data
 ****************************************************************************/

/*****************************************************************************
 * Private Functions
 ****************************************************************************/

PLUM_PRIVATE
E_GpioType_t hal_opl1x_gpio_dir(plum_hal_gpio_com_cof_t dir)
{
    E_GpioType_t type = IO_OUTPUT;
    switch (dir) {
        case PLUM_HAL_GPIO_MODE_ANALOG:
        case PLUM_HAL_GPIO_MODE_INPUT:
        case PLUM_HAL_GPIO_MODE_INPUT_UP:
        case PLUM_HAL_GPIO_MODE_INPUT_DOWN:
            type = IO_INPUT;
            break;

        case PLUM_HAL_GPIO_MODE_OUT_PP:
        case PLUM_HAL_GPIO_MODE_OUT_OD:
        case PLUM_HAL_GPIO_MODE_OUT_OD_UP:
        case PLUM_HAL_GPIO_MODE_OUT_OD_DOWN:
            type = IO_OUTPUT;
            break;

        default:
            break;
    }

    return (type);
}

PLUM_PRIVATE
E_GpioPull_t hal_opl1x_gpio_pull(plum_hal_gpio_com_cof_t dir)
{
    E_GpioPull_t pull = PULL_UP;
    switch (dir) {
        case PLUM_HAL_GPIO_MODE_ANALOG:
        case PLUM_HAL_GPIO_MODE_OUT_OD_DOWN:
        case PLUM_HAL_GPIO_MODE_INPUT_DOWN:
            pull = PULL_DOWN;
            break;

        case PLUM_HAL_GPIO_MODE_OUT_PP:
        case PLUM_HAL_GPIO_MODE_OUT_OD_UP:
        case PLUM_HAL_GPIO_MODE_INPUT_UP:
            pull = PULL_UP;
            break;

        default:
            break;
    }

    return (pull);
}

PLUM_PRIVATE
plum_u8 hal_opl1x_gpio_pin(plum_u32 id)
{
    plum_u8 idx = BLANK_PIN;

    switch (PLUM_HAL_GPIO_PIN(id)) {
#if defined(OPL1000_IO0_PIN)
        case 0:
            idx = OPL1000_IO0_PIN;
            break;
#endif
#if defined(OPL1000_IO1_PIN)
        case 1:
            idx = OPL1000_IO1_PIN;
            break;
#endif
#if defined(OPL1000_IO2_PIN)
        case 2:
            idx = OPL1000_IO2_PIN;
            break;
#endif
#if defined(OPL1000_IO3_PIN)
        case 3:
            idx = OPL1000_IO3_PIN;
            break;
#endif
#if defined(OPL1000_IO4_PIN)
        case 4:
            idx = OPL1000_IO4_PIN;
            break;
#endif
#if defined(OPL1000_IO5_PIN)
        case 5:
            idx = OPL1000_IO5_PIN;
            break;
#endif
#if defined(OPL1000_IO6_PIN)
        case 6:
            idx = OPL1000_IO6_PIN;
            break;
#endif
#if defined(OPL1000_IO7_PIN)
        case 7:
            idx = OPL1000_IO7_PIN;
            break;
#endif
#if defined(OPL1000_IO8_PIN)
        case 8:
            idx = OPL1000_IO8_PIN;
            break;
#endif
#if defined(OPL1000_IO9_PIN)
        case 9:
            idx = OPL1000_IO9_PIN;
            break;
#endif
#if defined(OPL1000_IO10_PIN)
        case 10:
            idx = OPL1000_IO10_PIN;
            break;
#endif
#if defined(OPL1000_IO11_PIN)
        case 11:
            idx = OPL1000_IO11_PIN;
            break;
#endif
#if defined(OPL1000_IO12_PIN)
        case 12:
            idx = OPL1000_IO12_PIN;
            break;
#endif
#if defined(OPL1000_IO13_PIN)
        case 13:
            idx = OPL1000_IO13_PIN;
            break;
#endif
#if defined(OPL1000_IO14_PIN)
        case 14:
            idx = OPL1000_IO14_PIN;
            break;
#endif
#if defined(OPL1000_IO15_PIN)
        case 15:
            idx = OPL1000_IO15_PIN;
            break;
#endif
#if defined(OPL1000_IO16_PIN)
        case 16:
            idx = OPL1000_IO16_PIN;
            break;
#endif
#if defined(OPL1000_IO17_PIN)
        case 17:
            idx = OPL1000_IO17_PIN;
            break;
#endif
#if defined(OPL1000_IO18_PIN)
        case 18:
            idx = OPL1000_IO18_PIN;
            break;
#endif
#if defined(OPL1000_IO19_PIN)
        case 19:
            idx = OPL1000_IO19_PIN;
            break;
#endif
#if defined(OPL1000_IO20_PIN)
        case 20:
            idx = OPL1000_IO20_PIN;
            break;
#endif
#if defined(OPL1000_IO21_PIN)
        case 21:
            idx = OPL1000_IO21_PIN;
            break;
#endif
#if defined(OPL1000_IO22_PIN)
        case 22:
            idx = OPL1000_IO22_PIN;
            break;
#endif
#if defined(OPL1000_IO23_PIN)
        case 23:
            idx = OPL1000_IO23_PIN;
            break;
#endif
        default:
            break;
    }

    return (idx);
}

PLUM_PRIVATE
hal_gpio_irq_t *hal_opl1x_gpio_irq_register(plum_u32 id,
                                            plum_void (*cb)(plum_u32 id))
{
    hal_gpio_irq_t *node = plum_null;

    do {
        if (!gpio_irq_head.list.next) {
            INIT_LIST_HEAD(&gpio_irq_head.list);
        }

        node = (hal_gpio_irq_t *)plum_hal_malloc(sizeof(hal_gpio_irq_t));
        if (!node) {
            break;
        }

        memset(node, 0, sizeof(hal_gpio_irq_t));
        node->func = cb;
        node->id   = id;

        list_add(&node->list, &gpio_irq_head.list);
    } while (0);

    return (node);
}

PLUM_PRIVATE
hal_gpio_irq_t *hal_opl1x_gpio_irq_find(plum_u32 id)
{
    hal_gpio_irq_t *node = plum_null;

    struct list_head *pos;
    list_for_each(pos, &gpio_irq_head.list)
    {
        node = list_entry(pos, hal_gpio_irq_t, list);
        if (node->id == id) {
            break;
        }
    }

    return (node);
}

PLUM_PRIVATE
plum_s32 hal_opl1x_gpio_irq_del(hal_gpio_irq_t *node)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!node) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        list_del(&node->list);
    } while (0);

    return (rc);
}

PLUM_PRIVATE
E_ItrType_t hal_opl1x_gpio_irq_settrip(plum_hal_gpio_irq_t trip)
{
    E_ItrType_t type = INT_TYPE_LEVEL;

    switch (trip) {
        case PLUM_HAL_GPIO_IRQ_FALLING_EDGE:
            type = INT_TYPE_FALLING_EDGE;
            break;

        case PLUM_HAL_GPIO_IRQ_RISING_EDGE:
            type = INT_TYPE_RISING_EDGE;
            break;

        case PLUM_HAL_GPIO_IRQ_BOTH_EDGE:
            type = INT_TYPE_BOTH_EDGE;
            break;

        case PLUM_HAL_GPIO_IRQ_HIGH_LEVEL:
        case PLUM_HAL_GPIO_IRQ_LOW_LEVEL:
            type = INT_TYPE_LEVEL;
            break;
        default:
            LOG_E("not support the trip way: %d", trip);
            break;
    }

    return (type);
}

PLUM_PRIVATE
plum_void hal_opl1x_gpio_irq_callback(E_GpioIdx_t idx)
{
    plum_u32 id = PLUM_HAL_GPIO_ID(A, idx);

    hal_gpio_irq_t *node = hal_opl1x_gpio_irq_find(id);
    if (!node) {
        if (!node->func) {
            node->func(id);
        }
    }
}

PLUM_PRIVATE
plum_s32 hal_opl1x_gpio_irq_init(plum_u32 id, plum_hal_gpio_irq_cof_t irq)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        hal_gpio_irq_t *node = hal_opl1x_gpio_irq_register(id, irq.irq_handle);
        if (!node) {
            rc = PLUM_ECODE_EMEM;
            LOG_E("hal_gpio_irq_apply err,rc: %d", rc);
            break;
        }

        E_GpioIdx_t idx = Hal_Pinmux_GetIO(hal_opl1x_gpio_pin(id));

        Hal_Vic_GpioDirection(idx, GPIO_INPUT);
        Hal_Vic_GpioCallBackFuncSet(idx, hal_opl1x_gpio_irq_callback);
        E_ItrType_t trip = hal_opl1x_gpio_irq_settrip(irq.trip);
        Hal_Vic_GpioIntTypeSel(idx, trip);
        Hal_Vic_GpioIntInv(idx, 0);
        Hal_Vic_GpioIntMask(idx, 0);
        Hal_Vic_GpioIntEn(idx, 1);
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 hal_opl1x_gpio_dir_init(plum_u32 id, plum_hal_gpio_com_cof_t com)
{
    plum_s32 rc = PLUM_ECODE_OK;

    T_OPL1000_Gpio gpio = {0};
    gpio.pin            = hal_opl1x_gpio_pin(id);
    gpio.type           = hal_opl1x_gpio_dir(com);
    gpio.pull           = hal_opl1x_gpio_pull(com);
    rc                  = Hal_Pinmux_Gpio_Init(&gpio);
    if (rc) {
        LOG_E("Hal_Pinmux_Gpio_Init err,rc: %d", rc);
    }

    return (rc);
}
/*****************************************************************************
 * Public Functions
 ****************************************************************************/

PLUM_PUBLIC
plum_s32 plum_hal_gpio_init(plum_u32 id, plum_hal_gpio_cof_t *cof)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!PLUM_HAL_ID_IS_GPIO(id)) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        if (cof->mode == PLUM_HAL_GPIO_MODE_COMMON) {
            rc = hal_opl1x_gpio_dir_init(id, cof->config.com);
        }
        else {
            rc = hal_opl1x_gpio_irq_init(id, cof->config.irq);
        }
        if (rc) {
            LOG_E("gpio init err,rc: %d", rc);
            break;
        }
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_bit plum_hal_gpio_read(plum_u32 id)
{
    E_GpioLevel_t sta;

    E_GpioIdx_t idx = Hal_Pinmux_GetIO(hal_opl1x_gpio_pin(id));

    sta = Hal_Vic_GpioInput(idx);

    return (plum_bit)(sta);
}

PLUM_PUBLIC
plum_s32 plum_hal_gpio_write(plum_u32 id, plum_hal_gpio_sta_t sta)
{
    plum_s32 rc = PLUM_ECODE_OK;

    E_GpioIdx_t idx = Hal_Pinmux_GetIO(hal_opl1x_gpio_pin(id));

    Hal_Vic_GpioOutput(
        idx, (sta == PLUM_HAL_GPIO_LOW) ? GPIO_LEVEL_LOW : GPIO_LEVEL_HIGH);

    return (rc);
}

PLUM_PUBLIC
plum_void plum_hal_gpio_irq_enable(plum_u32 id)
{
    hal_gpio_irq_t *node = hal_opl1x_gpio_irq_find(id);
    if (!node) {
        LOG_E("the id not registered a irq,id:%d", id);
        return;
    }

    E_GpioIdx_t idx = Hal_Pinmux_GetIO(hal_opl1x_gpio_pin(id));
    Hal_Vic_GpioIntEn(idx, 1);
}

PLUM_PUBLIC
plum_void plum_hal_gpio_irq_disable(plum_u32 id)
{
    hal_gpio_irq_t *node = hal_opl1x_gpio_irq_find(id);
    if (!node) {
        LOG_E("the id not registered a irq,id:%d", id);
        return;
    }

    E_GpioIdx_t idx = Hal_Pinmux_GetIO(hal_opl1x_gpio_pin(id));

    Hal_Vic_GpioIntEn(idx, 0);
}

/****************************************************************************/
/*                                                                          */
/*  End of file.                                                            */
/*                                                                          */
/****************************************************************************/
